package com.lx.boot.web;//说明:

/**
 * 创建人:游林夕/2019/5/8 17 10
 */

import com.lx.boot.OS;
import com.lx.constant.ConfigEunm;
import com.lx.constant.DefaultResult;
import com.lx.entity.Result;
import com.lx.util.exception.ResultServiceException;
import com.lx.util.exception.ServiceException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.sql.SQLIntegrityConstraintViolationException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


@Slf4j
@Configuration
public class LXWebConfigurer implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //是否开启拦截器
//        if (ConfigEunm.SECURITY_INTERCEPTOR_ENABLE.getBoolean()){
//            registry.addInterceptor(new DefaultSecurityInterceptor())
//                    .excludePathPatterns(DEFAULT_SERVER_NOTAUTHANDENCRYPTION_URLS)
//                    .addPathPatterns("/**");
//        }
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        if (ConfigEunm.WEB_EXCEPTION_HANDLER_ENABLE.getBoolean()){
            exceptionResolvers.add(getHandler());
        }
    }

    private HandlerExceptionResolver getHandler(){
        return new HandlerExceptionResolver() {
            @Override
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
                setRequestId(request);
                String method = request.getRequestURI();
                Result result = serviceExceptionLog(method, e);
                if (result != null) {
                    //业务失败的异常，如“账号或密码错误”
                } else if(e instanceof ConstraintViolationException){
                    result = DefaultResult.VIOLATION_ERR.setMessage("接口 ["+method+"] 参数验证出错:"+e);
                    log.error(result.getMessage(),e);
                }else if(e instanceof MethodArgumentNotValidException){
                    MethodArgumentNotValidException e1 = (MethodArgumentNotValidException) e;
                    String res = (String)e1.getBindingResult().getAllErrors().stream().map((cv) -> {
                        if (FieldError.class.isAssignableFrom(cv.getClass())){
                            FieldError err = (FieldError) cv;
                            return err == null ? "null" : err.getField() + ": " + cv.getDefaultMessage();
                        }else{
                            ObjectError err = cv;
                            return err == null ? "null" :  cv.getDefaultMessage();
                        }
                    }).collect(Collectors.joining(", "));
                    result = DefaultResult.VIOLATION_ERR.setMessage("接口 ["+method+"] 参数验证出错:"+res);
                    log.error(result.getMessage(),e);
                }else if(e instanceof org.springframework.web.bind.MissingServletRequestParameterException){
                    result = DefaultResult.MISS_ERR.setMessage("接口 ["+method+"] 缺少参数:"+ e.getMessage());
                    log.error(result.getMessage(),e);
                }else if(e instanceof org.springframework.http.converter.HttpMessageNotReadableException){
                    result = DefaultResult.PARS_ERR.setMessage("接口 ["+method+"] 参数解析异常:"+ e.getMessage());
                    log.error(result.getMessage(),e);
                }else if (e instanceof NoHandlerFoundException) {
                    result = DefaultResult.NO_HADNLER_ERR.setMessage("接口 [" + method + "] 不存在");
                    log.error(result.getMessage(),e);
                }else if(e instanceof org.apache.ibatis.reflection.ReflectionException){
                    result = DefaultResult.MISS_ERR.setMessage("接口 [" + method + "] 实体缺少字段!");
                    log.error(result.getMessage(),e);
                }else if (e instanceof DuplicateKeyException){ // 唯一键报错
                    Pattern pattern = Pattern.compile("Duplicate entry ['\"](.*?)['\"] for key");
                    Matcher matcher = pattern.matcher(e.getCause().getMessage());
                    if (matcher.find()) {
                        String data = matcher.group(1);// 返回第一个捕获组的内容
                        result = DefaultResult.RESULT_SERVICE_ERR.setMessage("["+data+"]数据已存在!");
                        log.error(result.getMessage(),e);
                    }
                }else if(e instanceof org.springframework.dao.DataIntegrityViolationException){
                    result = DefaultResult.RESULT_SERVICE_ERR.setMessage("接口 [" + method + "] 违反约束!"+e.getCause().getMessage());
                    log.error(result.getMessage(),e);

                }else if(e instanceof MultipartException){
                    result = DefaultResult.RESULT_SERVICE_ERR.setMessage("接口 [" + method + "] 上传文件异常!"+e.getCause().getMessage());
                    log.error(result.getMessage(),e);
                }else if (!"dev".equals(OS.getEnv())){
                    result = Result.err("网络异常,请稍后重试!");
                    log.error(result.getMessage(),e);
                }else {
                    String message = e.getCause()!=null && e.getCause().getMessage()!=null?e.getCause().getMessage():e.getMessage();
                    if (handler instanceof HandlerMethod) {
                        HandlerMethod handlerMethod = (HandlerMethod) handler;
                        message = String.format("接口 [%s] 出现异常，方法：%s.%s，异常摘要：%s",
                                request.getRequestURI(),
                                handlerMethod.getBean().getClass().getName(),
                                handlerMethod.getMethod().getName(),
                                message);
                    }
                    result = Result.err(message);
                    log.error(message, e);
                }
                OS.responseErrorResult(response,result);
                return new ModelAndView();
            }

        };
    }

    //说明:判断是否是自定义业务类型异常
    /**{ ylx } 2020/9/8 14:04 */
    private Result serviceExceptionLog(String method, Throwable e){
        do{
            if (e instanceof ServiceException){
                Result result;
                if (e instanceof ResultServiceException re){
                    result = re.getResult();
                }else{
                    result = DefaultResult.SERVICE_ERR.setMessage("网络异常,请稍后重试! ");
                }
                log.error("接口 ["+method+"] 业务层出错["+result.getCode()+"]=>",e);
                return result;
            }
        }while ((e = e.getCause()) != null);
        return null;
    }

    //说明: 设置追踪码
    /**{ ylx } 2021/2/3 15:37 */
    private void setRequestId(HttpServletRequest request) {
        try{
            if(MDC.get("requestId") == null && request != null){
                OS.setLogTraceId();
            }
        }catch (Exception e){
            log.error("解析日志信息错误",e);
        }
    }

}

